package com.demo.rpc.aspect;

import com.demo.holder.ThreadContextHolder;
import com.demo.rpc.connection.RPCTransactionalConnProxy;
import com.demo.rpc.manager.RPCTransactionalManager;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * Created by hunter
 *
 * @Description 监听获取数据库连接的切面
 */
@Aspect
@Component
public class ConnectionAspect {

    @Autowired
    private RPCTransactionalManager rpcTransactionalManager;

    /**
     * 拦截获取连接的地方, 变为代理连接返回
     *
     * @param joinPoint
     */
    @Around(value = "execution(* javax.sql.DataSource.getConnection(..))")
    public Connection around(ProceedingJoinPoint joinPoint) {
        try {
            Connection connection = (Connection) joinPoint.proceed();

            if (connection != null) {
                // 没被处理为代理类的, 这里变为代理类
                if (! (connection instanceof RPCTransactionalConnProxy)) {
                    // 最初是否自动提交配置
                    final boolean autoCommit = connection.getAutoCommit();
                    // 替换成我们的代理类
                    connection = new RPCTransactionalConnProxy(connection) {
                        // 由于需要访问到rpcTransactionalManager 所以这里用匿名方法来处理
                        @Override
                        public void commit() throws SQLException {
                            final String rpcTransactionId = ThreadContextHolder.getTransactionId();
                            // 当前线程没有分布式事务id, 可以直接提交
                            if (rpcTransactionId == null) {
                                System.out.println("~------ proxy commit ...");
                                this.connection.commit();
                            } else {
                                // 如果是当前应用发起的需要挂起等待
                                if (rpcTransactionalManager.isCurrentApplicationStart(rpcTransactionId)) {
                                    handleRPCTransactionalCommit(rpcTransactionalManager, rpcTransactionId, autoCommit);
                                } else { // 如果是其它应用发起的异步处理就好, 这里可以优化成线程池 TODO
                                    new Thread(() -> {
                                        try {
                                            handleRPCTransactionalCommit(rpcTransactionalManager, rpcTransactionId, autoCommit);
                                        } catch (SQLException e) {
                                            e.printStackTrace();
                                        }
                                    }).start();
                                }
                                ThreadContextHolder.removeTransactionId();
                            }
                        }
                    };
                }
            }

            return connection;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return null;
    }
}
